/*
 * Eugen Stoian <stoian.e@gmail.com> 2010
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

#include "Parameter.h"

Parameter::Parameter(const QString& name, const Interval& interval, int valuesCount, QObject * parent)
    :Object(parent),_interval(interval),_isAuto(true),_index(0) {
    _value = new Real;
    if(valuesCount < 2)
        valuesCount = 2;
    double delta = _interval.length()/((double)(valuesCount-1));
    _values.push_back(_interval.first());
    for(int i = 1; i < valuesCount-1;++i){
        _values.push_back(_interval.inf()+delta*i);
    }
    _values.push_back(_interval.last());
    *_value = _values.at(0);
    _outOfRange = false;
    setObjectName(name);
}



Parameter::~Parameter(){
    if(_value)
        delete _value;
}

void Parameter::reset() {
    if(_isAuto){
        _index = 0;
        _outOfRange = false;
        *_value = _values[0];
    }
}


Parameter& Parameter::operator++() {
    if(_isAuto){
        if(_index<_values.count()-1)
            *_value = _values.at(++_index);
        else
            _outOfRange = true;
    }
    return *this;
}

Parameter& Parameter::operator--() {
    if(_isAuto){
        if(_index>0)
            *_value = _values.at(--_index);
        else
            _outOfRange = true;
    }
    return *this;
}

Parameter& Parameter::operator+=(double val){
    (*_value) = (*_value) + val;
    if(_isAuto){
        if(!_interval.contains(*_value))
            _outOfRange = true;
        else
            _outOfRange = false;
    }
    return *this;
}

Parameter& Parameter::operator-=(double val){
    (*_value) = (*_value) - val;
    if(_isAuto){
        if(!_interval.contains(*_value))
            _outOfRange = true;
        else
            _outOfRange = false;
    }
    return *this;
}

void Parameter::setValue(double value) {
    if(_isAuto){
        if(_interval.contains(value)){
            *_value = value;
            if(!_interval.contains(*_value))
                _outOfRange = true;
            else
                _outOfRange = false;
            emit changed();
            emit valueChanged(value);
        }
    }
    else{
        *_value = value;
        emit changed();
        emit valueChanged(value);
    }
}

void Parameter::setAuto(bool a) {
    if(_isAuto!=a){
        _isAuto = a;
        reset();
        emit changed();
    }
}

void Parameter::setFixed(bool a) {
    setAuto(!a);
}

void Parameter::setValuesCount(int count) {
    _values.clear();
    if(count < 2)
        count = 2;
    double delta = _interval.length()/((double)(count-1));
    _values.push_back(_interval.first());
    for(int i = 1; i < count-1;++i){
        _values.push_back(_interval.inf()+delta*i);
    }
    _values.push_back(_interval.last());
    reset();
    emit changed();
}

void Parameter::setInterval(const Interval& interval){
    int count = _values.count();
    _values.clear();
    _interval = interval;
    double delta = _interval.length()/((double)(count-1));
    _values.push_back(_interval.first());
    for(int i = 1; i < count-1;++i){
        _values.push_back(_interval.inf()+delta*i);
    }
    _values.push_back(_interval.last());
    reset();
    emit changed();
}

Real Parameter::value() const {
    return *_value;
}

bool Parameter::isAuto() const{
    return _isAuto;
}


bool Parameter::isFixed() const{
    return !_isAuto;
}

const Interval &Parameter::interval() const {
    return _interval;
}

Interval &Parameter::interval() {
    return _interval;
}

int Parameter::valuesCount() const {
    if(_isAuto)
        return _values.count();
    return 1;
}

const Real *Parameter::data() const {
    return _value;
}

bool Parameter::isOutOfRange() const {
    return _isAuto && _outOfRange;
}

double Parameter::delta() const{
    if(_isAuto){
        double n = _values.count();
        if(_interval)
            return _interval.length()/n;
    }
    return 0.0;
}

Object* Parameter::clone() const{
    Parameter * p = new Parameter(this->objectName(),this->_interval,this->valuesCount());
    p->_index = _index;
    p->_isAuto = _isAuto;
    p->setValue(value());
    return p;
}

bool Parameter::isValid() const{
    if(_isAuto)
        return !isOutOfRange() && _interval;
    return true;
}

int Parameter::type() const{
    return Object::Param;
}

QDataStream& operator<<(QDataStream& stream, const Parameter& parameter){
    stream << parameter.objectName();
    stream << (QString)parameter._interval;
    stream << (int)parameter._values.count();
    stream << (bool)parameter._isAuto;
    stream << (double)*(parameter._value);
    return stream;
}

QDataStream& operator>>(QDataStream& stream, Parameter& parameter){
    QString intervalExpr;
    stream >> intervalExpr;
    parameter.setInterval(intervalExpr);
    int count;
    stream >> count;
    parameter.setValuesCount(count);
    stream >> parameter._isAuto;
    double value;
    stream >> value;
    parameter.setValue(value);
    parameter.reset();
    return stream;
}
